﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Diagnostics;
using System.Reflection;
using System.Windows.Forms;
using System.Collections;

namespace LogLib
{
    // 支持同步写和异步写的日记类库
    public class Log
    {
        /// <summary>
        /// 异常日记标记
        /// </summary>
        public readonly string ExceptionFlag = "Catch an exception";

        /// <summary>
        /// 边界线起始标记
        /// </summary>
        public static readonly string BeginingFlag = "Begin";

        /// <summary>
        /// 边界线结束标记
        /// </summary>
        public static readonly string EndingFlag = "End";

        private static char[] forbiddenChars = new char[] { 
            '\\', '/', ':',
            '*', '?', '"',
            '<', '>', '→', 
            '+', '[',']',
            '{','}',';',
            '!','~','`',
            '#','$','%',
            '^','&','*',
            '=',',','\''
        };

        private static bool _bInialized = false;
        public static bool Inialized
        {
            get { return _bInialized; }
        }

        public static string RootDirectory = "";

        private static Writer logWriter;

        private static AsyncWriter asyncWriter;

        private static Hashtable _htLogFiles = new Hashtable();

        private static Hashtable _htLogExceptionFiles = new Hashtable();

        private static Hashtable _htWebLogFiles = new Hashtable();

        private static LogErrorReportedEventHandler OnLogErrorReported;
        public static void SetErrorReportedEventHandler(LogErrorReportedEventHandler reported)
        {
            if (_bInialized && asyncWriter != null)
            {
                OnLogErrorReported = reported;
                asyncWriter.LogErrorReported += OnLogErrorReported;
            }
        }

        static Log()
        {
            Initialize();
        }

        public static void UpdateHashtableData()
        {
            //遍历哈希表中的键值  
            ArrayList arrayList = new ArrayList(_htLogFiles.Keys);
            for (int i = arrayList.Count-1; i >=0; i--)
            {
                Type type = arrayList[i] as Type;
                string className = type.FullName.Trim(forbiddenChars);
                string fullFilePath = CreatLogFilePath(className);
                _htLogFiles[type] = fullFilePath;
            }

            ArrayList arrayListEx = new ArrayList(_htLogExceptionFiles.Keys);
            for (int i = arrayListEx.Count - 1; i >= 0; i--)
            {
                Type type = arrayListEx[i] as Type;
                //string className = type.Name.Trim(forbiddenChars);
                string className = type.FullName.Trim(forbiddenChars);
                string fullFilePath = CreatExceptionLogFilePath(className); 
                _htLogExceptionFiles[type] = fullFilePath;
            }
        }

        /// <summary>
        /// 生成日记文件路径
        /// </summary>
        /// <returns></returns>
        private static string CreatLogFilePath(string className)
        {
            StringBuilder pathBuilder = new StringBuilder();
            char[] chars = className.ToCharArray();
            for (int i = 0; i < chars.Length; i++)
            {
                if(!forbiddenChars.Contains(chars[i]))
                {
                    pathBuilder.Append(chars[i]);
                }
                else
                {
                    pathBuilder.Append("_");
                }
            }
            try
            {
                return Path.Combine(LogConfig.TodayDirectory, pathBuilder.ToString() + ".log");
            }
            catch (Exception)
            {
                string uid = Guid.NewGuid().ToString("N");
                return Path.Combine(LogConfig.TodayDirectory, uid + ".log");
            }
        }

        /// <summary>
        /// 生成异常日记文件路径
        /// </summary>
        /// <returns></returns>
        private static string CreatExceptionLogFilePath(string className)
        {
            StringBuilder pathBuilder = new StringBuilder();
            char[] chars = className.ToCharArray();
            for (int i = 0; i < chars.Length; i++)
            {
                if (!forbiddenChars.Contains(chars[i]))
                {
                    pathBuilder.Append(chars[i]);
                }
                else
                {
                    pathBuilder.Append("_");
                }
            }
            try
            {
                return Path.Combine(LogConfig.TodayDirectory, pathBuilder.ToString() + "_Exception.log");
            }
            catch (Exception)
            {
                string uid = Guid.NewGuid().ToString("N");
                return Path.Combine(LogConfig.TodayDirectory, uid + "_Exception.log");
            }
        }

        private static void AddWebLogFilePath(string module)
        {
            lock (_htWebLogFiles.SyncRoot)
            {
                if (!_htWebLogFiles.ContainsKey(module))
                {
                    string fullFilePath = CreatLogFilePath(module);
                    _htWebLogFiles.Add(module, fullFilePath);
                }
            }
        }

        public static void SetDirectory(string dir)
        {
            LogConfig.SaveBaseDirectory(dir);
        }

        public static string GetDirectory()
        {
            return LogConfig.BaseDirectory;
        }

        public static void Initialize()
        {
            string directory = Path.GetDirectoryName(Assembly.GetExecutingAssembly().CodeBase);
            RootDirectory = directory.Substring(6);        
            if (!LogConfig.LoadConfig())
            {
                MessageBox.Show(
                    LogConfig.ErrorMessage, 
                    "日记配置选项错误", 
                MessageBoxButtons.OK, 
                MessageBoxIcon.Error);
                return;
            }
            logWriter = new Writer();
            asyncWriter = new AsyncWriter();
            asyncWriter.RunAsync();
            _bInialized = true;
        }

        public static void Exit()
        {
            if (_bInialized)
            {
                //logAsync.StopReceiver();
                //logAsync.KillAllReceivers();
                _bInialized = false;
            }         
        }

        /// <summary>
        /// 同步记录一条普通日记信息
        /// </summary>
        /// <param name="strLog"></param>
        public static void Write(string strLog)
        {
            if (!_bInialized)
            {
                return;
            }
            LogConfig.CheckLogFolder();
            logWriter.WriteLine(GetLogInfo(strLog));
        }

        private static LogInfo GetLogInfo(string strLog)
        {
            StackFrame frame = new StackFrame(2);
            MethodBase method = frame.GetMethod();
            Type type = method.DeclaringType;
            AddLogFilePath(type);
            string content = string.Format("{0} [类{1}-方法{2}]: {3}",
                DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss:ffffff"),
                type.FullName,
                method.Name,
                strLog);
            return new LogInfo(_htLogFiles[type] as string, content);
        }

        private static LogInfo GetLogInfo(string module, string strLog)
        {
            AddWebLogFilePath(module);
            string content = string.Format("{0} [Module-{1}]: {2}",
                DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss:ffffff"),
                module,
                strLog);
            return new LogInfo(_htWebLogFiles[module] as string, content);
        }

        /// <summary>
        /// 异步记录一条普通日记信息
        /// </summary>
        /// <param name="strLog"></param>
        public static void WriteAsync(string strLog)
        {
            if (!_bInialized)
            {
                return;
            }
            LogConfig.CheckLogFolder();
            asyncWriter.AddLogInfo(GetLogInfo(strLog));
        }

        /// <summary>
        /// 异步记录一条普通日记信息
        /// </summary>
        /// <param name="strLog"></param>
        public static void WriteAsync(string module, string strLog)
        {
            if (!_bInialized)
            {
                return;
            }
            LogConfig.CheckLogFolder();
            asyncWriter.AddLogInfo(GetLogInfo(module, strLog));
        }


        public static void Write(List<string> list)
        {
            if (!_bInialized)
            {
                return;
            }
            LogConfig.CheckLogFolder();
            StackFrame frame = new StackFrame(1);
            MethodBase method = frame.GetMethod();
            Type type = method.DeclaringType;
            AddLogFilePath(type);
            string strTime = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss:ffffff");
            List<string> logs = new List<string>();
            foreach (string strLog in list)
            {
                string strInfo = string.Format("{0} [类{1}-方法{2}]: {3}",
                    strTime,
                    type.FullName,
                    method.Name,
                    strLog);
                logs.Add(strInfo);               
            }
            string logName = _htLogFiles[type] as string;
            logWriter.WriteLines(new LogInfoList(logName, logs));
        }

        /// <summary>
        /// 记录一条异常信息
        /// </summary>
        /// <param name="strLog"></param>
        public static void WriteException(string strLog, bool isSync = true)
        {
            if (!_bInialized)
            {
                return;
            }
            LogConfig.CheckLogFolder();
            StackFrame frame = new StackFrame(1);
            MethodBase method = frame.GetMethod();
            Type type = method.DeclaringType;          
            AddLogExceptionFilePath(type);
            string content = string.Format("{0} [类{1}-方法{2}]: {3}",
                DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss:ffffff"),
                type.FullName,
                method.Name,
                strLog);
            LogInfo info = new LogInfo(_htLogExceptionFiles[type] as string, content);
            if (isSync)
            {
                logWriter.WriteLine(info);
            }
            else
            {
                asyncWriter.AddLogInfo(info);
            }
        }

        public static void AddBeginningBoundary(bool isSync = true)
        {
            if (!_bInialized)
            {
                return;
            }
            LogConfig.CheckLogFolder();
            StackFrame frame = new StackFrame(1);
            MethodBase method = frame.GetMethod();
            Type type = method.DeclaringType;         
            AddLogFilePath(type);
            string content = string.Format("{0} -----------------------------{1}------------------------------",
               DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss:ffffff"),
               BeginingFlag);
            LogInfo info = new LogInfo(_htLogFiles[type] as string, content);
            if (isSync)
            {
                logWriter.WriteLine(info);
            }
            else
            {
                asyncWriter.AddLogInfo(info);
            }
        }

        private static void AddLogFilePath(Type type)
        {
            lock (_htLogFiles.SyncRoot)
            {
                if (!_htLogFiles.ContainsKey(type))
                {
                    string[] array = type.FullName.Split(forbiddenChars, StringSplitOptions.RemoveEmptyEntries);
                    if (array.Length > 0)
                    {
                        string className = array[0];
                        string fullFilePath = Path.Combine(LogConfig.TodayDirectory, className + ".log");
                        _htLogFiles.Add(type, fullFilePath);
                    }                 
                }
            }           
        }

        private static void AddLogExceptionFilePath(Type type)
        {
            lock (_htLogExceptionFiles.SyncRoot)
            {
                if (!_htLogExceptionFiles.ContainsKey(type))
                {
                    string[] array = type.FullName.Split(forbiddenChars, StringSplitOptions.RemoveEmptyEntries);
                    if (array.Length > 0)
                    {
                        string className = array[0];
                        string fullFilePath = Path.Combine(LogConfig.TodayDirectory, className + "_Exception.log");
                        _htLogExceptionFiles.Add(type, fullFilePath);
                    }                  
                }
            }           
        }

        public static void AddEndingBoundary(bool isSync = true)
        {
            if (!_bInialized)
            {
                return;
            }
            StackFrame frame = new StackFrame(1);
            MethodBase method = frame.GetMethod();
            Type type = method.DeclaringType;
            AddLogFilePath(type);
            string content = string.Format("{0} -----------------------------{1}------------------------------",
               DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss:ffffff"),
               EndingFlag);
            LogInfo info = new LogInfo(_htLogFiles[type] as string, content);
            if (isSync)
            {
                logWriter.WriteLine(info);
            }
            else
            {
                asyncWriter.AddLogInfo(info);
            }
        }

        public static void AddBoundary(string strFlag, bool isSync = true)
        {
            if (!_bInialized)
            {
                return;
            }
            StackFrame frame = new StackFrame(1);
            MethodBase method = frame.GetMethod();
            Type type = method.DeclaringType;
            AddLogFilePath(type);
            string content = string.Format("{0} -----------------------------{1}------------------------------",
               DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss:ffffff"),
               strFlag);
            LogInfo info = new LogInfo(_htLogFiles[type] as string, content);
            if (isSync)
            {
                logWriter.WriteLine(info);
            }          
            else
            {
                asyncWriter.AddLogInfo(info);
            }
        }
    }
}
